创建时间: | 2015/10/8 23:41 |
来源: | http://java.9sssd.com/javabase/art/1043 |
[摘要]本文介绍Java类型信息,包括RTTI、Class对象、转换前检查等内容,并提供详细的示例代码供参考。
RTTI
为了实现变化与不便的解藕和,我们通常针对接口编程,在客户端调用的使用声明接口或者基类,然后引用到具体的类型,然后调用接口的方法,根据派生类型的动态绑定来执行特定对象的方法,这是多态。这样写出来的代码更加容易阅读书写和维护,因此多态通常作为面向对象编程的一大特点和编程目标。
但是如果在进行上塑造型之后,我们需要知道引用的具体类型来对不同的派生类执行不同的操作呢?这个时候就需要用到RTTI,运行时类型信息。
Class对象
要理解RIIT如何工作的,首先就要理解类型信息是如何在运行时保存的。Class对象是专门用来保存所有类的信息。Java通过使用Class对象来执行RTTI。对每个类都有一个对应的Class对象,因此每次编译一个新的类的时候,就会创建一个Class对象,保存在对应的.class文件里面。为了创建对应的class对象,Java虚拟机将会使用到一个叫做类加载器的东西。
类加载器系统能够组织一个类加载器链条,有一个初始的类加载器作为JVM应用的一部分。这个初始的加载器当作信任类来加载,包括Java的API类,通常是本地磁盘上的。一般情况下不需要额外的类加载器,但是如果你有特殊的需求,例如WEb服务器应用程序或者从网络上下载类来满足应用等,这个时候需要挂在额外的类加载器。
所有的类都是动态加载到JVM中的,在他们第一次使用的时候,这里的第一次指的是程序第一次调用类的静态成员,需要注意的是类的构造器也是静态的。当类加载器会首先检查Class对象是否加载,如果没有,那么找到同名的class文件,然后加载字节吗文件,验证代码的完整性和安全性。一旦这个类型的Class对象加载到内存中,它将会用来创建所有那个对象的类型。如下:
1 | //: typeinfo/SweetShop.java |
2 | // Examination of the way the class loader works. |
3 | import static net.mindview.util.Print.*; |
4 | class Candy { |
5 | static { print("Loading Candy"); } |
6 | } |
7 | class Gum { |
8 | static { print("Loading Gum"); } |
9 | } |
10 | class Cookie { |
11 | static { print("Loading Cookie"); } |
12 | } |
13 | public class SweetShop { |
14 | public static void main(String[] args) { |
15 | print("inside main"); |
16 | new Candy(); |
17 | print("After creating Candy"); |
18 | try { |
19 | Class.forName("Gum"); |
20 | } catch(ClassNotFoundException e) { |
21 | print("Couldn’t find Gum"); |
22 | } |
23 | print("After Class.forName(\"Gum\")"); |
24 | new Cookie(); |
25 | print("After creating Cookie"); |
26 | } |
27 | } /* Output: |
28 | inside main |
29 | Loading Candy |
30 | After creating Candy |
31 | Loading Gum |
32 | After Class.forName("Gum") |
33 | Loading Cookie |
34 | After creating Cookie |
35 | *///:~ |
在运行的任何时候想要获得类的信息都必须先使用Class获得类的引用,Class.forName是实现这一目的的很方便的方法,因为你不需要传递类的引用来获得类的信息,但是如果你有一个类的引用,你想要知道这个类的信息,那么调用基类Object的getCLass方法可以返回这个类型的具体类型的信息,另外还有很多其他的使用方法,如下:
1 | //: typeinfo/toys/ToyTest.java |
2 | // Testing class Class. |
3 | package typeinfo.toys; |
4 | import static net.mindview.util.Print.*; |
5 | interface HasBatteries {} |
6 | interface Waterproof {} |
7 | interface Shoots {} |
8 | class Toy { |
9 | // Comment out the following default constructor |
10 | // to see NoSuchMethodError from (*1*) |
11 | Toy() {} |
12 | Toy(int i) {} |
13 | } |
14 | class FancyToy extends Toy implements HasBatteries, Waterproof, Shoots { |
15 | FancyToy() { super(1); } |
16 | } |
17 | public class ToyTest { |
18 | static void printInfo(Class cc) { |
19 | print("Class name: " + cc.getName() + " is interface? [" + cc.isInterface() + "]"); |
20 | print("Simple name: " + cc.getSimpleName()); |
21 | print("Canonical name : " + cc.getCanonicalName()); |
22 | } |
23 | public static void main(String[] args) { |
24 | Class c = null; |
25 | try { |
26 | c = Class.forName("typeinfo.toys.FancyToy"); |
27 | } catch(ClassNotFoundException e) { |
28 | print("Can’t find FancyToy"); |
29 | System.exit(1); |
30 | } |
31 | printInfo(c); |
32 | for(Class face : c.getInterfaces()) |
33 | printInfo(face); |
34 | Class up = c.getSuperclass(); |
35 | Object obj = null; |
36 | try { |
37 | // Requires default constructor: |
38 | obj = up.newInstance(); |
39 | } catch(InstantiationException e) { |
40 | print("Cannot instantiate"); |
41 | System.exit(1); |
42 | } catch(IllegalAccessException e) { |
43 | print("Cannot access"); |
44 | System.exit(1); |
45 | } |
46 | printInfo(obj.getClass()); |
47 | } |
48 | } /* Output: |
49 | Class name: typeinfo.toys.FancyToy is interface? [false] |
50 | Simple name: FancyToy |
51 | Canonical name : typeinfo.toys.FancyToy |
52 | Class name: typeinfo.toys.HasBatteries is interface? [true] |
53 | Simple name: HasBatteries |
54 | Canonical name : typeinfo.toys.HasBatteries |
55 | Class name: typeinfo.toys.Waterproof is interface? [true] |
56 | Simple name: Waterproof |
57 | Canonical name : typeinfo.toys.Waterproof |
58 | Class name: typeinfo.toys.Shoots is interface? [true] |
59 | Simple name: Shoots |
60 | Canonical name : typeinfo.toys.Shoots |
61 | Class name: typeinfo.toys.Toy is interface? [false] |
62 | Simple name: Toy |
63 | Canonical name : typeinfo.toys.Toy |
64 | *///:~ |
另外,Java中提供了另外一种获取Class对象引用的方法,class属性,这个属性更加方便而且不许要放在try语句中,因为它不许要使用forName方法,而且更加有效率。这种方法适用于一般类和接口,包括初始类型,另外还可以对初始类型的封装类使用TYPE属性,它返回每个初始类型对应的Class对象,如下blloean.class==Bollean.TYPE;char.class==Character.TYPE;byte.class=Byte.TYPE;short.class=Short.TYPE;int.class==Integer.TYPE;long.class==Long.TYPE;float.class==Float.TYPE;double.class==Double.TYPE;void.class==Void.TYPE。建议使用.class属性来获取类型信息,因为这样使得对于一般类和初始值类型的兼容性更好。
使用class属性获取Class对象不会自动初始化Class对象,而是分为三步,首先加载,找到字节码从字节码中创建Class对象;连接,验证类的字节码,为静态字段分配空间,如果需要的话,创建这个类中对其他类的引用;初始化,如果有基类,初始化基类,执行静态初始化代码。初始化一直延迟到第一次引用静态方法,或者非常量静态字段:
1 | //: typeinfo/ClassInitialization.java |
2 | import java.util.*; |
3 | class Initable { |
4 | static final int staticFinal = 47; |
5 | static final int staticFinal2 = ClassInitialization.rand.nextInt(1000); |
6 | static { |
7 | System.out.println("Initializing Initable"); |
8 | } |
9 | } |
10 | class Initable2 { |
11 | static int staticNonFinal = 147; |
12 | static { |
13 | System.out.println("Initializing Initable2"); |
14 | } |
15 | } |
16 | class Initable3 { |
17 | static int staticNonFinal = 74; |
18 | static { |
19 | System.out.println("Initializing Initable3"); |
20 | } |
21 | } |
22 | public class ClassInitialization { |
23 | public static Random rand = new Random(47); |
24 | public static void main(String[] args) throws Exception { |
25 | Class initable = Initable.class; |
26 | System.out.println("After creating Initable ref"); |
27 | // Does not trigger initialization: |
28 | System.out.println(Initable.staticFinal); |
29 | // Does trigger initialization: |
30 | System.out.println(Initable.staticFinal2); |
31 | // Does trigger initialization: |
32 | System.out.println(Initable2.staticNonFinal); |
33 | Class initable3 = Class.forName("Initable3"); |
34 | System.out.println("After creating Initable3 ref"); |
35 | System.out.println(Initable3.staticNonFinal); |
36 | } |
37 | } /* Output: |
38 | After creating Initable ref |
39 | Initializing Initable |
40 | |
41 | Initializing Initable2 |
42 | |
43 | Initializing Initable3 |
44 | After creating Initable3 ref |
45 | |
46 | *///:~ |
Class还可以通过范型语法来指定获得的类型信息,如:
1 | //: typeinfo/GenericClassReferences.java |
2 | public class GenericClassReferences { |
3 | public static void main(String[] args) { |
4 | Class intClass = int.class; |
5 | Class<Integer> genericIntClass = int.class; |
6 | genericIntClass = Integer.class; // Same thing |
7 | intClass = double.class; |
8 | // genericIntClass = double.class; // Illegal |
9 | } |
10 | } ///:~ |
如果想要放宽到对任何类型的class属性都可以的话可以使用如下语法:
1 | //: typeinfo/WildcardClassReferences.java |
2 | public class WildcardClassReferences { |
3 | public static void main(String[] args) { |
4 | Class<?> intClass = int.class; |
5 | intClass = double.class; |
6 | } |
7 | } ///:~ |
这种语法与不用范型的效果一样,但是更加强调了一种编程的意向,那就是我是故意使得Class对象可以指向任何类型的对象信息的。那么如果想要使得对于范型和他的基类都适用的语法,如下:
1 | //: typeinfo/BoundedClassReferences.java |
2 | public class BoundedClassReferences { |
3 | public static void main(String[] args) { |
4 | Class<? extends Number> bounded = int.class; |
5 | bounded = double.class; |
6 | bounded = Number.class; |
7 | // Or anything else derived from Number. |
8 | } |
9 | } ///:~ |
使用范型语法的好处就是可以在编译的时候进行类型检查,这样可以更快的发现代码中的错误。在使用范型的时候有一个有趣的现象,那就是newInstance返回的是具体类型的对象,而不是基本的Object:
1 | //: typeinfo/toys/GenericToyTest.java |
2 | // Testing class Class. |
3 | package typeinfo.toys; |
4 | public class GenericToyTest { |
5 | public static void main(String[] args) throws Exception { |
6 | Class<FancyToy> ftClass = FancyToy.class; |
7 | // Produces exact type: |
8 | FancyToy fancyToy = ftClass.newInstance(); |
9 | Class<? super FancyToy> up = ftClass.getSuperclass(); |
10 | // This won’t compile: |
11 | // Class<Toy> up2 = ftClass.getSuperclass(); |
12 | // Only produces Object: |
13 | Object obj = up.newInstance(); |
14 | } |
15 | } ///:~ |
这里调用getSuperClass的时候范型只能指定为FancyToy的基类型,如语法?superFancyToy,而不是Toy,虽然编译器知道它的基类就是Toy,因为这样,所以up.newInstance不能返回具体的类型,而是Object对象。
Java SE5中Class还提供了cast方法,如下:
1 | //: typeinfo/ClassCasts.java |
2 | class Building {} |
3 | class House extends Building {} |
4 | public class ClassCasts { |
5 | public static void main(String[] args) { |
6 | Building b = new House(); |
7 | Class<House> houseType = House.class; |
8 | House h = houseType.cast(b); |
9 | h = (House)b; // ... or just do this. |
10 | } |
11 | } ///:~ |
另外还有Class.asSubclass方法将类型转换为更加具体的类型。
转换前检查
我们已经介绍了RTTI的两种形式,一种是类型转换,一种是Class类型,下面还有一种形式是instanceof,语法如下:
1 | if(x instanceof Dog) |
2 | ((Dog)x).bark(); |
下面是一个instanceof方法的使用实例,首先我们创建一个类的层级关系:
1 | //: typeinfo/pets/Person.java |
2 | package typeinfo.pets; |
3 | public class Person extends Individual { |
4 | public Person(String name) { super(name); } |
5 | } ///:~ |
6 | //: typeinfo/pets/Pet.java |
7 | package typeinfo.pets; |
8 | public class Pet extends Individual { |
9 | public Pet(String name) { super(name); } |
10 | public Pet() { super(); } |
11 | } ///:~ |
12 | //: typeinfo/pets/Dog.java |
13 | package typeinfo.pets; |
14 | public class Dog extends Pet { |
15 | public Dog(String name) { super(name); } |
16 | public Dog() { super(); } |
17 | } ///:~ |
18 | //: typeinfo/pets/Mutt.java |
19 | package typeinfo.pets; |
20 | public class Mutt extends Dog { |
21 | public Mutt(String name) { super(name); } |
22 | public Mutt() { super(); } |
23 | } ///:~ |
24 | //: typeinfo/pets/Pug.java |
25 | package typeinfo.pets; |
26 | public class Pug extends Dog { |
27 | public Pug(String name) { super(name); } |
28 | public Pug() { super(); } |
29 | } ///:~ |
30 | //: typeinfo/pets/Cat.java |
31 | package typeinfo.pets; |
32 | public class Cat extends Pet { |
33 | public Cat(String name) { super(name); } |
34 | public Cat() { super(); } |
35 | } ///:~ |
36 | //: typeinfo/pets/EgyptianMau.java |
37 | package typeinfo.pets; |
38 | public class EgyptianMau extends Cat { |
39 | public EgyptianMau(String name) { super(name); } |
40 | public EgyptianMau() { super(); } |
41 | } ///:~ |
42 | //: typeinfo/pets/Manx.java |
43 | package typeinfo.pets; |
44 | public class Manx extends Cat { |
45 | public Manx(String name) { super(name); } |
46 | public Manx() { super(); } |
47 | } ///:~ |
48 | //: typeinfo/pets/Cymric.java |
49 | package typeinfo.pets; |
50 | public class Cymric extends Manx { |
51 | public Cymric(String name) { super(name); } |
52 | public Cymric() { super(); } |
53 | } ///:~ |
54 | //: typeinfo/pets/Rodent.java |
55 | package typeinfo.pets; |
56 | public class Rodent extends Pet { |
57 | public Rodent(String name) { super(name); } |
58 | public Rodent() { super(); } |
59 | } ///:~ |
60 | //: typeinfo/pets/Rat.java |
61 | package typeinfo.pets; |
62 | public class Rat extends Rodent { |
63 | public Rat(String name) { super(name); } |
64 | public Rat() { super(); } |
65 | } ///:~ |
66 | //: typeinfo/pets/Mouse.java |
67 | package typeinfo.pets; |
68 | public class Mouse extends Rodent { |
69 | public Mouse(String name) { super(name); } |
70 | public Mouse() { super(); } |
71 | } ///:~ |
72 | //: typeinfo/pets/Hamster.java |
73 | package typeinfo.pets; |
74 | public class Hamster extends Rodent { |
75 | public Hamster(String name) { super(name); } |
76 | public Hamster() { super(); } |
77 | } ///:~ |
然后我们创建一个可以随意生成Pet实例的数组:
1 | //: typeinfo/pets/PetCreator.java |
2 | // Creates random sequences of Pets. |
3 | package typeinfo.pets;import java.util.*; |
4 | public abstract class PetCreator { |
5 | private Random rand = new Random(47); |
6 | // The List of the different types of Pet to create: |
7 | public abstract List<Class<? extends Pet>> types(); |
8 | public Pet randomPet() { // Create one random Pet |
9 | int n = rand.nextInt(types().size()); |
10 | try { |
11 | return types().get(n).newInstance(); |
12 | } catch(InstantiationException e) { |
13 | throw new RuntimeException(e); |
14 | } catch(IllegalAccessException e) { |
15 | throw new RuntimeException(e); |
16 | } |
17 | } |
18 | public Pet[] createArray(int size) { |
19 | Pet[] result = new Pet[size]; |
20 | for(int i = 0; i < size; i++) |
21 | result[i] = randomPet(); |
22 | return result; |
23 | } |
24 | public ArrayList<Pet> arrayList(int size) { |
25 | ArrayList<Pet> result = new ArrayList<Pet>(); |
26 | Collections.addAll(result, createArray(size)); |
27 | return result; |
28 | } |
29 | } ///:~ |
1 | //: typeinfo/pets/ForNameCreator.java |
2 | package typeinfo.pets; |
3 | import java.util.*; |
4 | public class ForNameCreator extends PetCreator { |
5 | private static List<Class<? extends Pet>> types = new ArrayList<Class<? extends Pet>>(); |
6 | // Types that you want to be randomly created: |
7 | private static String[] typeNames = { |
8 | "typeinfo.pets.Mutt","typeinfo.pets.Pug","typeinfo.pets.EgyptianMau","typeinfo.pets.Manx", |
9 | "typeinfo.pets.Cymric","typeinfo.pets.Rat","typeinfo.pets.Mouse","typeinfo.pets.Hamster" |
10 | }; |
11 | @SuppressWarnings("unchecked") |
12 | private static void loader() { |
13 | try { |
14 | for(String name : typeNames) |
15 | types.add((Class<? extends Pet>)Class.forName(name)); |
16 | } catch(ClassNotFoundException e) { |
17 | throw new RuntimeException(e); |
18 | } |
19 | } |
20 | static { loader(); } |
21 | public List<Class<? extends Pet>> types() {return types;} |
22 | } ///:~ |
下面就可以通过Map来对每个具体的类型进行计数:
1 | //: typeinfo/PetCount.java |
2 | // Using instanceof. |
3 | import typeinfo.pets.*; |
4 | import java.util.*; |
5 | import static net.mindview.util.Print.*; |
6 | public class PetCount { |
7 | static class PetCounter extends HashMap<String,Integer> { |
8 | public void count(String type) { |
9 | Integer quantity = get(type); |
10 | if(quantity == null) |
11 | put(type, 1); |
12 | else |
13 | put(type, quantity + 1); |
14 | } |
15 | } |
16 | public static void countPets(PetCreator creator) { |
17 | PetCounter counter= new PetCounter(); |
18 | for(Pet pet : creator.createArray(20)) { |
19 | // List each individual pet: |
20 | printnb(pet.getClass().getSimpleName() + " "); |
21 | if(pet instanceof Pet) |
22 | counter.count("Pet"); |
23 | if(pet instanceof Dog) |
24 | counter.count("Dog"); |
25 | if(pet instanceof Mutt) |
26 | counter.count("Mutt"); |
27 | if(pet instanceof Pug) |
28 | counter.count("Pug"); |
29 | if(pet instanceof Cat) |
30 | counter.count("Cat"); |
31 | if(pet instanceof Manx) |
32 | counter.count("EgyptianMau"); |
33 | if(pet instanceof Manx) |
34 | counter.count("Manx"); |
35 | if(pet instanceof Manx) |
36 | counter.count("Cymric"); |
37 | if(pet instanceof Rodent) |
38 | counter.count("Rodent"); |
39 | if(pet instanceof Rat) |
40 | counter.count("Rat"); |
41 | if(pet instanceof Mouse) |
42 | counter.count("Mouse"); |
43 | if(pet instanceof Hamster) |
44 | counter.count("Hamster"); |
45 | } |
46 | // Show the counts: |
47 | print(); |
48 | print(counter); |
49 | } |
50 | public static void main(String[] args) { |
51 | countPets(new ForNameCreator()); |
52 | } |
53 | } /* Output: |
54 | Rat Manx Cymric Mutt Pug Cymric Pug Manx Cymric Rat EgyptianMau Hamster |
55 | EgyptianMau Mutt Mutt Cymric Mouse Pug Mouse Cymric |
56 | {Pug=3, Cat=9, Hamster=1, Cymric=7, Mouse=2, Mutt=3, Rodent=5, Pet=20, |
57 | Manx=7, EgyptianMau=7, Dog=6, Rat=2} |
58 | *///:~ |
也可以使用class属性来实现PetCreator:
1 | //: typeinfo/pets/LiteralPetCreator.java |
2 | // Using class literals. |
3 | package typeinfo.pets; |
4 | import java.util.*; |
5 | public class LiteralPetCreator extends PetCreator { |
6 | // No try block needed. |
7 | @SuppressWarnings("unchecked") |
8 | public static final List<Class<? extends Pet>> allTypes = Collections.unmodifiableList(Arrays.asList( |
9 | Pet.class, Dog.class, Cat.class, Rodent.class,Mutt.class, Pug.class, EgyptianMau.class, Manx.class, |
10 | Cymric.class, Rat.class, Mouse.class,Hamster.class)); |
11 | // Types for random creation: |
12 | private static final List<Class<? extends Pet>> types = allTypes.subList(allTypes.indexOf(Mutt.class), |
13 | allTypes.size()); |
14 | public List<Class<? extends Pet>> types() { |
15 | return types; |
16 | } |
17 | public static void main(String[] args) { |
18 | System.out.println(types); |
19 | } |
20 | } /* Output: |
21 | [class typeinfo.pets.Mutt, class typeinfo.pets.Pug, class |
22 | typeinfo.pets.EgyptianMau, class typeinfo.pets.Manx, class |
23 | typeinfo.pets.Cymric, class typeinfo.pets.Rat, class |
24 | typeinfo.pets.Mouse, class typeinfo.pets.Hamster] |
25 | *///:~ |
现在我们有了两种PetCreator的实现,为了将第二种方法设置为默认的实现方式,可以使用Facade模式:
1 | //: typeinfo/pets/Pets.java |
2 | // Facade to produce a default PetCreator. |
3 | package typeinfo.pets; |
4 | import java.util.*; |
5 | public class Pets { |
6 | public static final PetCreator creator = new LiteralPetCreator(); |
7 | public static Pet randomPet() { |
8 | return creator.randomPet(); |
9 | } |
10 | public static Pet[] createArray(int size) { |
11 | return creator.createArray(size); |
12 | } |
13 | public static ArrayList<Pet> arrayList(int size) { |
14 | return creator.arrayList(size); |
15 | } |
16 | } ///:~ |
然后定义PetCount2输出与PetCount同样的结果:
1 | //: typeinfo/PetCount2.java |
2 | import typeinfo.pets.*; |
3 | public class PetCount2 { |
4 | public static void main(String[] args) { |
5 | PetCount.countPets(Pets.creator); |
6 | } |
7 | } /* (Execute to see output) *///:~ |
Class.instance方法还提供了一种动态检测对象类型的方法,如下:
1 | //: typeinfo/PetCount3.java |
2 | // Using isInstance() |
3 | import typeinfo.pets.*; |
4 | import java.util.*; |
5 | import net.mindview.util.*; |
6 | import static net.mindview.util.Print.*; |
7 | public class PetCount3 { |
8 | static class PetCounter extends LinkedHashMap<Class<? extends Pet>,Integer> { |
9 | public PetCounter() { |
10 | super(MapData.map(LiteralPetCreator.allTypes, 0)); |
11 | } |
12 | public void count(Pet pet) { |
13 | // Class.isInstance() eliminates instanceofs: |
14 | for(Map.Entry<Class<? extends Pet>,Integer> pair : entrySet()) |
15 | if(pair.getKey().isInstance(pet)) |
16 | put(pair.getKey(), pair.getValue() + 1); |
17 | } |
18 | public String toString() { |
19 | StringBuilder result = new StringBuilder("{"); |
20 | for(Map.Entry<Class<? extends Pet>,Integer> pair : entrySet()) { |
21 | result.append(pair.getKey().getSimpleName()); |
22 | result.append("="); |
23 | result.append(pair.getValue()); |
24 | result.append(", "); |
25 | } |
26 | result.delete(result.length()-2, result.length()); |
27 | result.append("}"); |
28 | return result.toString(); |
29 | } |
30 | } |
31 | public static void main(String[] args) { |
32 | PetCounter petCount = new PetCounter(); |
33 | for(Pet pet : Pets.createArray(20)) { |
34 | printnb(pet.getClass().getSimpleName() + " "); |
35 | petCount.count(pet); |
36 | } |
37 | print(); |
38 | print(petCount); |
39 | } |
40 | } /* Output: |
41 | Rat Manx Cymric Mutt Pug Cymric Pug Manx Cymric Rat EgyptianMau Hamster |
42 | EgyptianMau Mutt Mutt Cymric Mouse Pug Mouse Cymric |
43 | {Pet=20, Dog=6, Cat=9, Rodent=5, Mutt=3, Pug=3, EgyptianMau=2, Manx=7, |
44 | Cymric=5, Rat=2, Mouse=2, Hamster=1} |
45 | *///:~ |
在PetCount3中要预先加载所有的Pet类型,除了这个方法,我们可以使用Class.isAssignableFrom方法,,然后创建一个通用的计数方法:
1 | //: net/mindview/util/TypeCounter.java |
2 | // Counts instances of a type family. |
3 | package net.mindview.util; |
4 | import java.util.*; |
5 | public class TypeCounter extends HashMap<Class<?>,Integer>{ |
6 | private Class<?> baseType; |
7 | public TypeCounter(Class<?> baseType) { |
8 | this.baseType = baseType; |
9 | } |
10 | public void count(Object obj) { |
11 | Class<?> type = obj.getClass(); |
12 | if(!baseType.isAssignableFrom(type)) |
13 | throw new RuntimeException(obj + " incorrect type: " + type + ", should be type or subtype of " + baseType); |
14 | countClass(type); |
15 | } |
16 | private void countClass(Class<?> type) { |
17 | Integer quantity = get(type); |
18 | put(type, quantity == null ? 1 : quantity + 1); |
19 | Class<?> superClass = type.getSuperclass(); |
20 | if(superClass != null && baseType.isAssignableFrom(superClass)) |
21 | countClass(superClass); |
22 | } |
23 | public String toString() { |
24 | StringBuilder result = new StringBuilder("{"); |
25 | for(Map.Entry<Class<?>,Integer> pair : entrySet()) { |
26 | result.append(pair.getKey().getSimpleName()); |
27 | result.append("="); |
28 | result.append(pair.getValue()); |
29 | result.append(", "); |
30 | } |
31 | result.delete(result.length()-2, result.length()); |
32 | result.append("}"); |
33 | return result.toString(); |
34 | } |
35 | } ///:~ |
1 | //: typeinfo/PetCount4.java |
2 | import typeinfo.pets.*; |
3 | import net.mindview.util.*; |
4 | import static net.mindview.util.Print.*; |
5 | public class PetCount4 { |
6 | public static void main(String[] args) { |
7 | TypeCounter counter = new TypeCounter(Pet.class); |
8 | for(Pet pet : Pets.createArray(20)) { |
9 | printnb(pet.getClass().getSimpleName() + " "); |
10 | counter.count(pet); |
11 | } |
12 | print(); |
13 | print(counter); |
14 | } |
15 | } /* Output: (Sample) |
16 | Rat Manx Cymric Mutt Pug Cymric Pug Manx Cymric Rat EgyptianMau Hamster |
17 | EgyptianMau Mutt Mutt Cymric Mouse Pug Mouse Cymric |
18 | {Mouse=2, Dog=6, Manx=7, EgyptianMau=2, Rodent=5, Pug=3, Mutt=3, |
19 | Cymric=5, Cat=9, Hamster=1, Pet=20, Rat=2} |
20 | *///:~ |